package com.link.base.tencent.corpwx.corppayment.service;

import com.link.base.tencent.corpwx.addressbook.service.AddressBookUserService;
import com.link.base.tencent.corpwx.corppayment.model.CorpPayment;
import com.link.base.tencent.corpwx.corppayment.model.CorpRedPack;
import com.link.base.wechat.payment.util.PayUtils;
import com.link.core.basic.dao.KeyGenerateDao;
import com.link.core.basic.service.BasicServiceException;
import com.link.core.basic.service.BasicServiceImpl;
import com.link.core.util.StringUtils;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import com.thoughtworks.xstream.io.xml.XppDriver;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;


/**
 * 企业支付服务
 *
 * @author liying
 * @date 2019/2/21
 */
@Service
public class CorpPaymentServiceImpl extends BasicServiceImpl<CorpPayment> implements CorpPaymentService {

    /**
     * 发送企业红包api请求地址
     */
    private static final String SEND_RED_PACK_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendworkwxredpack";

    @Resource
    private AddressBookUserService addressBookUserService;

    @Resource
    private KeyGenerateDao keyGenerateDao;

    /**
     * 发放企业红包
     *
     * @author liying
     * @date 2019/02/21
     * @param record 传入参数
     */
    @Override
    public List<Map> sendCorpRedPack(CorpRedPack record) throws Exception {
        // 企业微信api
        String url = SEND_RED_PACK_URL;
        List<Map> mapList = new ArrayList<>();

        // 获取openId
        if (StringUtils.isNotBlank(StringUtils.toString(record.getAgentId())) && record.getUserIdList().size() != 0) {
            for (String userId :
                    record.getUserIdList()) {
                Map<String, Object> map;
                String openId = addressBookUserService.transformOpenId(userId, record.getAgentId());
                record.setReOpenid(openId);
                // 获取随机字符串
                String nonceStr = this.getRandomStr(22);
                record.setNonceStr(nonceStr);
                // 获取订单编号
                String mchBillNo = record.getMchId().concat(keyGenerateDao.orderNoGenerate());
                record.setMchBillNo(mchBillNo);
                // 获取企业微信签名
                String workWXSign = this.getSign(record, "WXCorp");
                record.setWorkWXSign(workWXSign);
                // 获取微信支付签名
                String sign = this.getSign(record, "WXPay");
                record.setSign(sign);

                // 将java实体类转换为xml字符串，用XStream xStream = new XStream()方法会存在下划线的情况会产生两条下划线，如 "user_id" 会转换成 "user__id"
                XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("__", "_")));
                // 忽略不需要转换为xml字节的字段
                xStream.omitField(CorpRedPack.class, "userIdList");
                if (StringUtils.isBlank(StringUtils.toString(record.getAgentId()))) {
                    xStream.omitField(CorpRedPack.class, "agentId");
                } else {
                    xStream.omitField(CorpRedPack.class, "senderName");
                    xStream.omitField(CorpRedPack.class, "senderHeaderMediaId");
                }
                // 使用注解进行转换，得到请求api参数
                xStream.processAnnotations(record.getClass());
                String xmlParam = xStream.toXML(record);
                map = PayUtils.post(xmlParam, url, record.getMchId());
                // 返回结果插入结果集
                mapList.add(map);
            }
        } else {
            throw new BasicServiceException("无法获取openId，确认传入数据是否正确！");
        }

        return mapList;
    }

    /**
     * 获取签名（微信支付 + 企业微信）
     *
     * @author liying
     * @date 2019/02/21
     * @param record 传入参数
     * @param type   签名类型（微信支付 or 企业微信）
     */
    private String getSign(CorpRedPack record, String type) throws Exception {
        // 定义sortMap类型，保证map数据有序性(字典序)
        SortedMap<Object, Object> payMap = new TreeMap<Object, Object>();
        SortedMap<Object, Object> CorpMap = new TreeMap<Object, Object>();
        // 拼接到签名字符串后的key值
        String key = "xoBZi7adcpkZfxKOjew8FK3JpSQKmbdO";
        // 企业微信签名拼接，企业支付secret
        String secret = "N1jW0MUibO3gFb5ao0WbsG-STYJ-qHhex2Ig6vp6l74";
        String sign;    // 签名
        String signType = "WXPay";
        if (StringUtils.isNotBlank(record.getNonceStr())) {
            payMap.put("nonce_str", record.getNonceStr());
            CorpMap.put("nonce_str", record.getNonceStr());
        }
        if (StringUtils.isNotBlank(record.getMchBillNo())) {
            payMap.put("mch_billno", record.getMchBillNo());
            CorpMap.put("mch_billno", record.getMchBillNo());
        }
        if (StringUtils.isNotBlank(record.getMchId())) {
            payMap.put("mch_id", record.getMchId());
            CorpMap.put("mch_id", record.getMchId());
        }
        if (StringUtils.isNotBlank(record.getWxAppId())) {
            payMap.put("wxappid", record.getWxAppId());
            CorpMap.put("wxappid", record.getWxAppId());
        }
        // agentId 和 （sender_name && sender_header_media_id），二者只能选一个作为参数
        if (StringUtils.isNotBlank(StringUtils.toString(record.getAgentId()))) {
            payMap.put("agentid", record.getAgentId());
        } else {
            if (StringUtils.isNotBlank(record.getSenderName()) && StringUtils.isNotBlank(record.getSenderHeaderMediaId())) {
                payMap.put("sender_name", record.getSenderName());
                payMap.put("sender_header_media_id", record.getSenderHeaderMediaId());
            } else {
                throw new BasicServiceException("请传入正确的agentId或用户！");
            }
        }
        if (StringUtils.isNotBlank(record.getReOpenid())) {
            payMap.put("re_openid", record.getReOpenid());
            CorpMap.put("re_openid", record.getReOpenid());
        }
        if (StringUtils.isNotBlank(StringUtils.toString(record.getTotalAmount()))) {
            payMap.put("total_amount", record.getTotalAmount());
            CorpMap.put("total_amount", record.getTotalAmount());
        }
        if (StringUtils.isNotBlank(record.getWishing())) {
            payMap.put("wishing", record.getWishing());
        }
        if (StringUtils.isNotBlank(record.getActName())) {
            payMap.put("act_name", record.getActName());
            CorpMap.put("act_name", record.getActName());
        }
        if (StringUtils.isNotBlank(record.getRemark())) {
            payMap.put("remark", record.getRemark());
        }
        if (StringUtils.isNotBlank(record.getSceneId())) {
            payMap.put("scene_id", record.getSceneId());
        }
        if (StringUtils.isNotBlank(record.getWorkWXSign())) {
            payMap.put("workwx_sign", record.getWorkWXSign());
        }

        String signStr;
        // 微信支付签名拼接key；企业微信签名拼接企业支付secret
        if (signType.equals(type)) {
            signStr = this.appendString(payMap);
            sign = PayUtils.md5Encode(signStr.concat("&key=").concat(key)).toUpperCase();
        } else {
            signStr = this.appendString(CorpMap);
            sign = PayUtils.md5Encode(signStr.concat("&secret=").concat(secret)).toUpperCase();
        }

        return sign;
    }


    /**
     * 拼接签名
     *
     * @author liying
     * @date 2019/02/21
     * @param map 传入参数
     */
    public String appendString(Map map) {
        StringBuffer sb = new StringBuffer();
        Set es = map.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();

            sb.append(k + "=" + v + "&");
        }
        String signStr = sb.toString().substring(0, sb.toString().length() - 1);

        return signStr;
    }

    /**
     * 生成指定位数的随机数
     *
     * @author liying
     * @date 2019/02/21
     * @param num 指定位数
     */
    public String getRandomStr(int num) {
        String base = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < num; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
}
